
#include <scheduler.h>
#include <process.h>

static process_t *current_user_process;
thread_t *g_current_user_thread;

byte g_current_quantum;

static thread_t *ready_queue[PRIORITIES];
static thread_t *ready_queue_tail[PRIORITIES];
static thread_t *blocked_queue;
static thread_t *blocked_queue_tail;
// probably need to add a couple more (eg. swapped out but ready?)

/**
 * Initialize the scheduler
 */
int init_scheduler(void)
{
	int i;
    for (i = 0; i < PRIORITIES; i++)
    {
    	ready_queue[i] = ready_queue_tail[i] = NULL;
    }
    blocked_queue = blocked_queue_tail = NULL;    

	current_user_process = proc_get_sysprocess();
	g_current_user_thread = proc_get_systhread();
	
	g_current_quantum = g_current_user_thread->quantum;
	
	return 0;
}

/**
 * Schedule a thread
 * 
 * thread_t *thread - threat to be run
 */
void scheduler(thread_t *thread, byte priority)
{
	debugf("[SCHED] Scheduling thread #%d\n", thread->tid);
	
	if (thread->state == STATE_KILLED)
	{
		// cleanup
		proc_destroy_thread(thread);
	}
	
	thread->state = STATE_READY;
	
	if (ready_queue_tail[priority] == NULL)  // empty queue
	{
		ready_queue[priority] = ready_queue_tail[priority] = thread;
		thread->next_thread = NULL;
	}
	else
	{
		ready_queue_tail[priority]->next_thread = thread;
		ready_queue_tail[priority] = thread;
		thread->next_thread = NULL;
	}
}

/**
 * Select a thread to run next
 */
void dispatcher(void)
{
	thread_t *thread = NULL;
	int prio = PRIORITY_MAX;
	while ((thread == NULL) && (prio >= PRIORITY_MIN))
	{
		if (ready_queue[prio] != NULL)
		{
			// de-queue
			thread = ready_queue[prio];
			ready_queue[prio] = thread->next_thread;
			
			if (ready_queue_tail[prio] == thread)  // last process in the queue?
				ready_queue_tail[prio] = NULL;
		}
		else
		{
			prio--;
		}
		
		// check to make sure this process is ok
		if (thread->state == STATE_KILLED)
		{
			// cleanup
			proc_destroy_thread(thread);
			thread = NULL;  // find another...
		}
	}
	
	thread->state = STATE_RUNNING;
	g_current_quantum = thread->quantum;
	g_current_user_thread = thread;
	
	debugf("[SCHED] Dispatched thread #%d\n", thread->tid);
	
	// change CR3 here?
	asm volatile("movl %0, %%cr3" : : "r"(thread->process->pd));
}
